Projekt Dokumentation

Aufgabe: Vorhersage der Umsätze vom 9.6.2019 bis 30.07.2019

Infos zu den gegebenen Daten

Warengruppen: * 1 = Brot * 2 = Brötchen * 3 = Croissant * 4 = Konditorei * 5 = Kuchen * 6 = Saisonbrot

Saisonbrot muss nicht vorhergesagt werden! Siehe ‘predition_template.csv’.

Wetterdaten: * Mittlerer Bewölkungsgrad am Tag (0 = min, 8 = max) * MIttlere Temperatur in C * Mittlere Windgeschwindigkeit in m/s * Wettercode (http://www.seewetter-kiel.de/seewetter/daten_symbole.htm) * und in der Datei wettercodes.Rda

Vorbereitung & benötigte Libraries laden

remove(list = ls())
# Create list with needed libraries
pkgs <- c("lubridate", "stringr","tidyverse", "readr", 
          "fastDummies", "reticulate", "ggplot2", "Metrics", "VIM")

# Load each listed library and check if it is installed and install if necessary
for (pkg in pkgs) {
  if (!require(pkg, character.only = TRUE)) {
    install.packages(pkg)
    library(pkg, character.only = TRUE)
  }
}

Vorbereitete Datensätze laden

  • Wetterdaten wurden in “Datenaufbereitung_Wetter.Rmd” vorbereitet
  • Feiertagedaten wurden in “Datenaufbereitung_Feiertage.R” vorbereitet
  • Schulferien wurden in “Datenaufbereitung_Schulferien.R” vorbereitet
  • Umsatzdaten wurden in “Datenaufbereitung_Umsatz.R” vorbereitet
# Lade Daten
load("pj_wetter_dummy.Rda")
pj_wetter <- pj_wetter_dummy
  
load("kiwoDT.Rda")
pj_kiwo <- kiwoDT
  
load("pj_umsatz.Rda")

load("schulferien.Rda")
pj_schulferien <- schulferien

# Erste Betrachtung der Daten
#summary(pj_wetter)
#summary(pj_kiwo)
#summary(pj_umsatz)

allData_dummy

# Merge erstellt automatisch die Schnittmenge
# Der Zusatz all.x = TRUE sorgt dafür, dass keine Zeilen (basierend auf Datensatz x) weggelöscht werden
# Wetterdaten nach Datum hinzufügen
pj_umsatz_wetter <- merge(pj_umsatz, pj_wetter, by="Datum", all.x = TRUE)

# Schulferien nach Datum hinzufügen
pj_umsatz_wetter_ferien <- merge(pj_umsatz_wetter, pj_schulferien, by="Datum", all.x = TRUE)

# KiWo nach Datum hinzufügen
allData <- merge(pj_umsatz_wetter_ferien, pj_kiwo, by="Datum", all.x = TRUE)

# auf fehlende Werte überprüfen:
allData_na <- allData %>%
  aggr(combined=TRUE, numbers=TRUE)
Warning: not enough horizontal space to display frequencies

# Imputation Temperatur und Windstaerke
# Aktuell: "Datenspende" vom Wert vom Vortag
# ZIEL: Mittelwert aus Temperatur von Vortag und Tag danach -> Armando! :)
allData <- allData %>%  
  hotdeck(variable = c("Temperatur", "Windstaerke"),
          ord_var = "Datum")

#imputierte Werte graphisch überprüfen:
ggplot(allData) +
  geom_point(aes(x = Datum, y = Temperatur, color = Temperatur_imp))

ggplot(allData) +
  geom_point(aes(x = Datum, y = Windstaerke, color = Windstaerke_imp))


# NA Wettercodes zu 0, da Spalte WC_NA angibt, wo Wettercodes gefehlt haben
# Spalten 12 -24

# das gleiche gilt bei der Bewölkung
# Spalten 26 - 29

# weitere NA mit 0 füllen, dort wo es Sinn ergibt  

allData <- allData %>%
    mutate_at(c(12:34), ~replace(., is.na(.), 0))

# dummy coding der Wochentage
allData_dummy <- dummy_cols(allData, select_columns = "Wochentag")

allData_dummy$year <- year(allData_dummy$Datum)
allData_dummy$month <- month(allData_dummy$Datum)
allData_dummy$day <- day(allData_dummy$Datum)

allData_dummy$Datum <- NULL

summary(allData_dummy)
      Brot           Brötchen        Croissant        Konditorei         Kuchen         Saisonbrot    
 Min.   : 23.11   Min.   : 175.0   Min.   : 37.74   Min.   : 27.43   Min.   : 121.5   Min.   :  0.00  
 1st Qu.: 98.14   1st Qu.: 286.8   1st Qu.:106.79   1st Qu.: 66.12   1st Qu.: 231.1   1st Qu.:  0.00  
 Median :121.89   Median : 367.8   Median :143.90   Median : 79.67   Median : 268.2   Median :  0.00  
 Mean   :124.40   Mean   : 398.9   Mean   :164.52   Mean   : 87.12   Mean   : 278.2   Mean   : 11.02  
 3rd Qu.:147.06   3rd Qu.: 486.1   3rd Qu.:205.06   3rd Qu.: 97.41   3rd Qu.: 309.1   3rd Qu.:  0.00  
 Max.   :416.79   Max.   :1203.4   Max.   :565.94   Max.   :430.50   Max.   :1879.5   Max.   :172.87  
  Wochentag         Konditorei_imp   Windstaerke      Temperatur     WC_Bewölkung_abnehmend
 Length:2123        Mode :logical   Min.   : 2.00   Min.   :-8.475   Min.   :0             
 Class :character   FALSE:2069      1st Qu.: 5.00   1st Qu.: 6.312   1st Qu.:0             
 Mode  :character   TRUE :54        Median : 5.00   Median :11.650   Median :0             
                                    Mean   : 5.58   Mean   :12.031   Mean   :0             
                                    3rd Qu.: 6.00   3rd Qu.:17.769   3rd Qu.:0             
                                    Max.   :12.00   Max.   :32.671   Max.   :0             
 WC_Bewölkung_gleichbleibend WC_Bewölkung_nicht_beobachtet WC_Bewölkung_zunehmend WC_Dunst_Staub   
 Min.   :0                   Min.   :0.00000               Min.   :0.000000       Min.   :0.00000  
 1st Qu.:0                   1st Qu.:0.00000               1st Qu.:0.000000       1st Qu.:0.00000  
 Median :0                   Median :0.00000               Median :0.000000       Median :0.00000  
 Mean   :0                   Mean   :0.09279               Mean   :0.000471       Mean   :0.06924  
 3rd Qu.:0                   3rd Qu.:0.00000               3rd Qu.:0.000000       3rd Qu.:0.00000  
 Max.   :0                   Max.   :1.00000               Max.   :1.000000       Max.   :1.00000  
 WC_Ereignisse_letzte_h  WC_Gewitter      WC_Nebel_Eisnebel    WC_Regen        WC_Schauer   WC_Schnee      
 Min.   :0.0000         Min.   :0.00000   Min.   :0.00000   Min.   :0.0000   Min.   :0    Min.   :0.00000  
 1st Qu.:0.0000         1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.0000   1st Qu.:0    1st Qu.:0.00000  
 Median :0.0000         Median :0.00000   Median :0.00000   Median :0.0000   Median :0    Median :0.00000  
 Mean   :0.1512         Mean   :0.01319   Mean   :0.01413   Mean   :0.3151   Mean   :0    Mean   :0.01978  
 3rd Qu.:0.0000         3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:1.0000   3rd Qu.:0    3rd Qu.:0.00000  
 Max.   :1.0000         Max.   :1.00000   Max.   :1.00000   Max.   :1.0000   Max.   :0    Max.   :1.00000  
 WC_Sprühregen      WC_Trockenereignisse     WC_NA        Bewoelkungsgrad_gering Bewoelkungsgrad_keine
 Min.   :0.000000   Min.   :0.00000      Min.   :0.0000   Min.   :0.0000         Min.   :0.00         
 1st Qu.:0.000000   1st Qu.:0.00000      1st Qu.:0.0000   1st Qu.:0.0000         1st Qu.:0.00         
 Median :0.000000   Median :0.00000      Median :0.0000   Median :0.0000         Median :0.00         
 Mean   :0.004239   Mean   :0.07772      Mean   :0.2346   Mean   :0.1182         Mean   :0.13         
 3rd Qu.:0.000000   3rd Qu.:0.00000      3rd Qu.:0.0000   3rd Qu.:0.0000         3rd Qu.:0.00         
 Max.   :1.000000   Max.   :1.00000      Max.   :1.0000   Max.   :1.0000         Max.   :1.00         
 Bewoelkungsgrad_mittel Bewoelkungsgrad_stark Bewoelkungsgrad_NA  Schulferien      KielerWoche    
 Min.   :0.0000         Min.   :0.0000        Min.   :0.000000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000         1st Qu.:0.0000        1st Qu.:0.000000   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :0.0000         Median :0.0000        Median :0.000000   Median :0.0000   Median :0.0000  
 Mean   :0.3627         Mean   :0.3773        Mean   :0.004239   Mean   :0.2398   Mean   :0.0212  
 3rd Qu.:1.0000         3rd Qu.:1.0000        3rd Qu.:0.000000   3rd Qu.:0.0000   3rd Qu.:0.0000  
 Max.   :1.0000         Max.   :1.0000        Max.   :1.000000   Max.   :1.0000   Max.   :1.0000  
 Temperatur_imp     Windstaerke_imp    Wochentag_Friday Wochentag_Monday Wochentag_Saturday Wochentag_Sunday
 Min.   :0.000000   Min.   :0.000000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000     Min.   :0.0000  
 1st Qu.:0.000000   1st Qu.:0.000000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000     1st Qu.:0.0000  
 Median :0.000000   Median :0.000000   Median :0.0000   Median :0.0000   Median :0.0000     Median :0.0000  
 Mean   :0.007536   Mean   :0.007536   Mean   :0.1413   Mean   :0.1423   Mean   :0.1441     Mean   :0.1432  
 3rd Qu.:0.000000   3rd Qu.:0.000000   3rd Qu.:0.0000   3rd Qu.:0.0000   3rd Qu.:0.0000     3rd Qu.:0.0000  
 Max.   :1.000000   Max.   :1.000000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000     Max.   :1.0000  
 Wochentag_Thursday Wochentag_Tuesday Wochentag_Wednesday      year          month            day      
 Min.   :0.0000     Min.   :0.0000    Min.   :0.0000      Min.   :2013   Min.   : 1.00   Min.   : 1.0  
 1st Qu.:0.0000     1st Qu.:0.0000    1st Qu.:0.0000      1st Qu.:2014   1st Qu.: 3.50   1st Qu.: 8.0  
 Median :0.0000     Median :0.0000    Median :0.0000      Median :2016   Median : 7.00   Median :16.0  
 Mean   :0.1432     Mean   :0.1432    Mean   :0.1427      Mean   :2016   Mean   : 6.52   Mean   :15.7  
 3rd Qu.:0.0000     3rd Qu.:0.0000    3rd Qu.:0.0000      3rd Qu.:2017   3rd Qu.:10.00   3rd Qu.:23.0  
 Max.   :1.0000     Max.   :1.0000    Max.   :1.0000      Max.   :2019   Max.   :12.00   Max.   :31.0  
save(allData_dummy, file="projectData_dummy.Rda")

Testdatensatz

# Erstelle einen leeren Dataframe mit einer Spalte für das Datum
testDatenSatz <- data.frame(Datum = character())

# Erstelle eine Sequenz von Daten im angegebenen Zeitraum
datum_sequenz <- seq(from = as.Date("2019-06-09"),
                     to = as.Date("2019-07-30"),
                     by = "days")

# Füge die Daten der Sequenz dem Dataframe hinzu
testDatenSatz <- rbind(testDatenSatz, data.frame(Datum = datum_sequenz))
testDatenSatz$Wochentag <- weekdays(testDatenSatz$Datum)
testDatenSatz <- merge(testDatenSatz, pj_wetter, by="Datum", all.x = TRUE)
testDatenSatz <- merge(testDatenSatz, pj_schulferien, by="Datum", all.x = TRUE)
testDatenSatz <- merge(testDatenSatz, pj_kiwo, by="Datum", all.x = TRUE)
### Testdatensatz ###

testDatenSatz <- testDatenSatz %>% 
  hotdeck(variable = c("Temperatur", "Windstaerke"),
          ord_var = "Datum")

#imputierte Werte von testDatenSatz graphisch überprüfen:
ggplot(testDatenSatz) +
  geom_point(aes(x = Datum, y = Temperatur, color = Temperatur_imp))

ggplot(testDatenSatz) +
  geom_point(aes(x = Datum, y = Windstaerke, color = Windstaerke_imp))


testDatenSatz <- testDatenSatz %>%
    mutate_at(c(4:26), ~replace(., is.na(.), 0))

# dummy coding der Wochentage
testDatenSatz <- dummy_cols(testDatenSatz, select_columns = "Wochentag")

testDatenSatz$year <- year(testDatenSatz$Datum)
testDatenSatz$month <- month(testDatenSatz$Datum)
testDatenSatz$day <- day(testDatenSatz$Datum)

testDatenSatz$Datum <- NULL

summary(testDatenSatz)
  Wochentag          Windstaerke      Temperatur    WC_Bewölkung_abnehmend WC_Bewölkung_gleichbleibend
 Length:52          Min.   :3.000   Min.   :14.46   Min.   :0              Min.   :0                  
 Class :character   1st Qu.:5.000   1st Qu.:16.93   1st Qu.:0              1st Qu.:0                  
 Mode  :character   Median :6.000   Median :19.59   Median :0              Median :0                  
                    Mean   :5.788   Mean   :20.41   Mean   :0              Mean   :0                  
                    3rd Qu.:7.000   3rd Qu.:23.40   3rd Qu.:0              3rd Qu.:0                  
                    Max.   :9.000   Max.   :29.73   Max.   :0              Max.   :0                  
 WC_Bewölkung_nicht_beobachtet WC_Bewölkung_zunehmend WC_Dunst_Staub   WC_Ereignisse_letzte_h  WC_Gewitter    
 Min.   :0.0000                Min.   :0              Min.   :0.0000   Min.   :0.0000         Min.   :0.0000  
 1st Qu.:0.0000                1st Qu.:0              1st Qu.:0.0000   1st Qu.:0.0000         1st Qu.:0.0000  
 Median :0.0000                Median :0              Median :0.0000   Median :0.0000         Median :0.0000  
 Mean   :0.1538                Mean   :0              Mean   :0.1346   Mean   :0.1538         Mean   :0.1154  
 3rd Qu.:0.0000                3rd Qu.:0              3rd Qu.:0.0000   3rd Qu.:0.0000         3rd Qu.:0.0000  
 Max.   :1.0000                Max.   :0              Max.   :1.0000   Max.   :1.0000         Max.   :1.0000  
 WC_Nebel_Eisnebel    WC_Regen        WC_Schauer   WC_Schnee WC_Sprühregen     WC_Trockenereignisse     WC_NA       
 Min.   :0         Min.   :0.0000   Min.   :0    Min.   :0   Min.   :0.00000   Min.   :0.00000      Min.   :0.0000  
 1st Qu.:0         1st Qu.:0.0000   1st Qu.:0    1st Qu.:0   1st Qu.:0.00000   1st Qu.:0.00000      1st Qu.:0.0000  
 Median :0         Median :0.0000   Median :0    Median :0   Median :0.00000   Median :0.00000      Median :0.0000  
 Mean   :0         Mean   :0.1731   Mean   :0    Mean   :0   Mean   :0.01923   Mean   :0.01923      Mean   :0.2308  
 3rd Qu.:0         3rd Qu.:0.0000   3rd Qu.:0    3rd Qu.:0   3rd Qu.:0.00000   3rd Qu.:0.00000      3rd Qu.:0.0000  
 Max.   :0         Max.   :1.0000   Max.   :0    Max.   :0   Max.   :1.00000   Max.   :1.00000      Max.   :1.0000  
 Bewoelkungsgrad_gering Bewoelkungsgrad_keine Bewoelkungsgrad_mittel Bewoelkungsgrad_stark Bewoelkungsgrad_NA
 Min.   :0.00000        Min.   :0.0000        Min.   :0.0000         Min.   :0.0000        Min.   :0         
 1st Qu.:0.00000        1st Qu.:0.0000        1st Qu.:0.0000         1st Qu.:0.0000        1st Qu.:0         
 Median :0.00000        Median :0.0000        Median :0.0000         Median :0.0000        Median :0         
 Mean   :0.05769        Mean   :0.1346        Mean   :0.4615         Mean   :0.3462        Mean   :0         
 3rd Qu.:0.00000        3rd Qu.:0.0000        3rd Qu.:1.0000         3rd Qu.:1.0000        3rd Qu.:0         
 Max.   :1.00000        Max.   :1.0000        Max.   :1.0000         Max.   :1.0000        Max.   :0         
  Schulferien      KielerWoche     Temperatur_imp Windstaerke_imp Wochentag_Friday Wochentag_Monday
 Min.   :0.0000   Min.   :0.0000   Min.   :0      Mode :logical   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0      FALSE:52        1st Qu.:0.0000   1st Qu.:0.0000  
 Median :1.0000   Median :0.0000   Median :0                      Median :0.0000   Median :0.0000  
 Mean   :0.5769   Mean   :0.1731   Mean   :0                      Mean   :0.1346   Mean   :0.1538  
 3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:0                      3rd Qu.:0.0000   3rd Qu.:0.0000  
 Max.   :1.0000   Max.   :1.0000   Max.   :0                      Max.   :1.0000   Max.   :1.0000  
 Wochentag_Saturday Wochentag_Sunday Wochentag_Thursday Wochentag_Tuesday Wochentag_Wednesday      year     
 Min.   :0.0000     Min.   :0.0000   Min.   :0.0000     Min.   :0.0000    Min.   :0.0000      Min.   :2019  
 1st Qu.:0.0000     1st Qu.:0.0000   1st Qu.:0.0000     1st Qu.:0.0000    1st Qu.:0.0000      1st Qu.:2019  
 Median :0.0000     Median :0.0000   Median :0.0000     Median :0.0000    Median :0.0000      Median :2019  
 Mean   :0.1346     Mean   :0.1538   Mean   :0.1346     Mean   :0.1538    Mean   :0.1346      Mean   :2019  
 3rd Qu.:0.0000     3rd Qu.:0.0000   3rd Qu.:0.0000     3rd Qu.:0.0000    3rd Qu.:0.0000      3rd Qu.:2019  
 Max.   :1.0000     Max.   :1.0000   Max.   :1.0000     Max.   :1.0000    Max.   :1.0000      Max.   :2019  
     month            day       
 Min.   :6.000   Min.   : 1.00  
 1st Qu.:6.000   1st Qu.:11.00  
 Median :7.000   Median :17.50  
 Mean   :6.577   Mean   :17.19  
 3rd Qu.:7.000   3rd Qu.:24.00  
 Max.   :7.000   Max.   :30.00  
save(testDatenSatz, file="Datenaufbereitung_Testdaten.Rda")

Features & Labels

features <- c("day",                           "month",                         "year",
              "Windstaerke",                   "Temperatur",                    "WC_Bewölkung_abnehmend",
              "WC_Bewölkung_gleichbleibend",   "WC_Bewölkung_nicht_beobachtet", "WC_Bewölkung_zunehmend",
              "WC_Dunst_Staub",                "WC_Ereignisse_letzte_h",        "WC_Gewitter",
              "WC_Nebel_Eisnebel",             "WC_Regen",                      "WC_Schauer",
              "WC_Schnee",                     "WC_Sprühregen",                 "WC_Trockenereignisse",
              "WC_NA",                         "Bewoelkungsgrad_gering",        "Bewoelkungsgrad_keine",
              "Bewoelkungsgrad_mittel",        "Bewoelkungsgrad_stark",         "Bewoelkungsgrad_NA",
              "Schulferien",                   "KielerWoche",                   "Wochentag_Tuesday",          
              "Wochentag_Thursday",
              "Wochentag_Friday",              "Wochentag_Wednesday",           "Wochentag_Monday",
              "Wochentag_Saturday",            "Wochentag_Sunday")

labels <- c("Brot", "Brötchen", "Croissant", "Konditorei", "Kuchen")

Selection of Training, Validation and Test Data

# Setting the random counter to a fixed value, so the random initialization stays the same (the random split is always the same)
set.seed(1)

assignment <- sample(1:2, size = nrow(allData_dummy), prob = c(.8, .2), replace = TRUE)

# Create training, validation and test data for the features and the labels
training_features <- allData_dummy[assignment == 1, features]    
training_labels <- allData_dummy[assignment == 1, labels]    
training_labels <- as.data.frame(training_labels)

validation_features <- allData_dummy[assignment == 2, features]  
validation_labels <- allData_dummy[assignment == 2, labels]  
validation_labels <- as.data.frame(validation_labels)

testing_features <- testDatenSatz %>% select(features)

#are there any missing values?
table(is.na(training_features))

FALSE 
55770 
table(is.na(validation_features))

FALSE 
14289 
table(is.na(testing_features))

FALSE 
 1716 
#summary(allData_dummy)

Modell aufstellen in Python

reticulate::repl_python()
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import InputLayer, Dense, BatchNormalization, Dropout
from tensorflow.keras.optimizers import Adam

# The argument "input_shape" for the definition of the input layer must include 
# the number of input variables (features) used for the model. 
# To automatically calculate this number we use the function `r.training_features.keys()`, 
# which returns the list of variable names of the dataframe `training_features`.
# Then, the funtion `len()` returns the length of this list of variable names 
# (i.e. the number of variables in the input)

model = Sequential([
  InputLayer(input_shape = (len(r.training_features.keys()), )),
  BatchNormalization(),
  Dense(len(r.training_features.keys()), activation = 'swish'),
  Dropout(0.2),
  Dense(len(r.training_features.keys()), activation = 'swish'),
  Dropout(0.2),
  Dense(len(r.training_features.keys()), activation = 'swish'),
  Dense(5)
])

# Ausgabe einer ZUsammenfassung zur Form des MOdells, das geschätzt wird (nicht notwendig)
#model.summary()

Schätzung de neuronalen Netzes

# definition of the loss function and the optimazation function with hyperparameters
model.compile(loss="mape", optimizer=Adam(learning_rate=0.001))

#Schätzung des Modells
history = model.fit(r.training_features, r.training_labels, epochs = 250,
                    validation_data = (r.validation_features, r.validation_labels), verbose = 0)
                    
model.save("python_model.h5")

graphische Ausgabe der Modelloptimierung

quit
# Graphische Ausgabe der Modelloptimierung

#create data
data <- data.frame(val_loss = unlist(py$history$history$val_loss),
                   loss = unlist(py$history$history$loss))

ggplot(data[-(1:10), ])+
  geom_line(aes(x = 1:length(val_loss), y = val_loss, colour = "Validation Loss")) +
  geom_line(aes(x = 1:length(loss), y = loss, colour = "Training Loss")) +
  scale_colour_manual(values = c("Training Loss"="blue", "Validation Loss" = "red")) +
  labs(title = "Loss Function Values During Optimazation") +
  xlab("Iteration Number") +
  ylab("Loss")

Auswertung der Schätzergebnisse

# Schätzung der (normierten) Preise für die Trainings- und Testdaten
training_predictions <- py$model$predict(training_features)

 1/53 [..............................] - ETA: 1s
53/53 [==============================] - 0s 336us/step
validation_predictions <- py$model$predict(validation_features)

 1/14 [=>............................] - ETA: 0s
14/14 [==============================] - 0s 326us/step
testing_predictions <- py$model$predict(testing_features)

1/2 [==============>...............] - ETA: 0s
2/2 [==============================] - 0s 471us/step
# Vergleich der Gütekriterien für die Traingings- und Testdaten
a <- format(mape(training_labels[,1], training_predictions[,1])*100, digits=3, nsmall=2)
b <- format(mape(training_labels[,2], training_predictions[,2])*100, digits=3, nsmall=2)
c <- format(mape(training_labels[,3], training_predictions[,3])*100, digits=3, nsmall=2)
d <- format(mape(training_labels[,4], training_predictions[,4])*100, digits=3, nsmall=2)
e <- format(mape(training_labels[,5], training_predictions[,5])*100, digits=3, nsmall=2)

cat(paste0("\nMAPE on the Training Data1:\t", a))

MAPE on the Training Data1: 17.87
cat(paste0("\nMAPE on the Training Data2:\t", b))

MAPE on the Training Data2: 11.48
cat(paste0("\nMAPE on the Training Data3:\t", c))

MAPE on the Training Data3: 17.22
cat(paste0("\nMAPE on the Training Data4:\t", d))

MAPE on the Training Data4: 20.49
cat(paste0("\nMAPE on the Training Data5:\t", e, "\n"))

MAPE on the Training Data5: 13.37
g <- format(mape(validation_labels[,1], validation_predictions[,1])*100, digits=3, nsmall=2)
h <- format(mape(validation_labels[,2], validation_predictions[,2])*100, digits=3, nsmall=2)
i <- format(mape(validation_labels[,3], validation_predictions[,3])*100, digits=3, nsmall=2)
j <- format(mape(validation_labels[,4], validation_predictions[,4])*100, digits=3, nsmall=2)
k <- format(mape(validation_labels[,5], validation_predictions[,5])*100, digits=3, nsmall=2)
  
cat(paste0("\nMAPE on the Validation Data1:\t", g))

MAPE on the Validation Data1:   19.26
cat(paste0("\nMAPE on the Validation Data2:\t", h))

MAPE on the Validation Data2:   13.49
cat(paste0("\nMAPE on the Validation Data3:\t", i))

MAPE on the Validation Data3:   20.83
cat(paste0("\nMAPE on the Validation Data4:\t", j))

MAPE on the Validation Data4:   20.22
cat(paste0("\nMAPE on the Validation Data5:\t", k, "\n"))

MAPE on the Validation Data5:   14.42
# Mean of Training and Validation Data MAPE
meanT <- c(as.double(a), as.double(b), as.double(c), as.double(d), as.double(e)) 
meanV <- c(as.double(g), as.double(h), as.double(i), as.double(j), as.double(k)) 

cat(paste0("\nMean Training MAPE: ", mean(meanT), "\n"))

Mean Training MAPE: 16.086
cat(paste0("Mean Validation MAPE: ", mean(meanV), "\n"))
Mean Validation MAPE: 17.644

Grafischer vergleich der vorhergesagten & tatsächlicher Preise für die Trainings- und Validierungsdaten

data_train <- data.frame(prediction = training_predictions[,1], actual = training_labels[,1])
data_val <- data.frame(prediction = validation_predictions[,1], actual = validation_labels[,1])
data_test <- data.frame(prediction = testing_predictions[,1])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train[]) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 1") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val[,]) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 1") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 1") +
  xlab("Case Number") +
  ylab("Price in EUR") 


#------------------------- 2 -------------------------#

data_train2 <- data.frame(prediction = training_predictions[,2], actual = training_labels[,2])
data_val2 <- data.frame(prediction = validation_predictions[,2], actual = validation_labels[,2])
data_test2 <- data.frame(prediction = testing_predictions[,2])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train2) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 2") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val2) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 2") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test2) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 2") +
  xlab("Case Number") +
  ylab("Price in EUR") 


#------------------------- 3 -------------------------#

data_train3 <- data.frame(prediction = training_predictions[,3], actual = training_labels[,3])
data_val3 <- data.frame(prediction = validation_predictions[,3], actual = validation_labels[,3])
data_test3 <- data.frame(prediction = testing_predictions[,3])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train3) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 3") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val3) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 3") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test3) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 3") +
  xlab("Case Number") +
  ylab("Price in EUR") 



#------------------------- 4 -------------------------#

data_train4 <- data.frame(prediction = training_predictions[,4], actual = training_labels[,4])
data_val4 <- data.frame(prediction = validation_predictions[,4], actual = validation_labels[,4])
data_test4 <- data.frame(prediction = testing_predictions[,4])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train4) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 4") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val4) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 4") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test4) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 4") +
  xlab("Case Number") +
  ylab("Price in EUR") 


#------------------------- 5 -------------------------#

data_train5 <- data.frame(prediction = training_predictions[,5], actual = training_labels[,5])
data_val5 <- data.frame(prediction = validation_predictions[,5], actual = validation_labels[,5])
data_test5 <- data.frame(prediction = testing_predictions[,5])

# Plot der Ergebnisse der Trainingsdaten
ggplot(data_train5) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Training Data 5") +
  xlab("Case Number") +
  ylab("Price in EUR") 


# Plot der Ergebnisse der Validierungsdaten
ggplot(data_val5) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  geom_line( aes(x=1:length(actual), y=actual, colour = "Actual Values" )) +
  scale_colour_manual( values = c("Predicted Values"="blue", "Actual Values"="red") ) +
  labs(title="Predicted and Actual Values for the Validation Data 5") +
  xlab("Case Number") +
  ylab("Price in EUR")


# Plot der Ergebnisse der Testdaten
ggplot(data_test5) +
  geom_line( aes(x=1:length(prediction), y=prediction, colour = "Predicted Values" )) +
  labs(title="Prediction for the Test Data 5") +
  xlab("Case Number") +
  ylab("Price in EUR") 

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCiMgUHJvamVrdCBEb2t1bWVudGF0aW9uCgpBdWZnYWJlOiBWb3JoZXJzYWdlIGRlciBVbXPDpHR6ZSB2b20gOS42LjIwMTkgYmlzIDMwLjA3LjIwMTkKCiMjIyBJbmZvcyB6dSBkZW4gZ2VnZWJlbmVuIERhdGVuCgpXYXJlbmdydXBwZW46IFwqIDEgPSBCcm90IFwqIDIgPSBCcsO2dGNoZW4gXCogMyA9IENyb2lzc2FudCBcKiA0ID0KS29uZGl0b3JlaSBcKiA1ID0gS3VjaGVuIFwqIDYgPSBTYWlzb25icm90CgojIyMgU2Fpc29uYnJvdCBtdXNzIG5pY2h0IHZvcmhlcmdlc2FndCB3ZXJkZW4hIFNpZWhlICdwcmVkaXRpb25fdGVtcGxhdGUuY3N2Jy4KCldldHRlcmRhdGVuOiBcKiBNaXR0bGVyZXIgQmV3w7Zsa3VuZ3NncmFkIGFtIFRhZyAoMCA9IG1pbiwgOCA9IG1heCkgXCoKTUl0dGxlcmUgVGVtcGVyYXR1ciBpbiBDIFwqIE1pdHRsZXJlIFdpbmRnZXNjaHdpbmRpZ2tlaXQgaW4gbS9zIFwqCldldHRlcmNvZGUgKDxodHRwOi8vd3d3LnNlZXdldHRlci1raWVsLmRlL3NlZXdldHRlci9kYXRlbl9zeW1ib2xlLmh0bT4pClwqIHVuZCBpbiBkZXIgRGF0ZWkgd2V0dGVyY29kZXMuUmRhCgojIyMgVm9yYmVyZWl0dW5nICYgYmVuw7Z0aWd0ZSBMaWJyYXJpZXMgbGFkZW4KCmBgYHtyfQpyZW1vdmUobGlzdCA9IGxzKCkpCiMgQ3JlYXRlIGxpc3Qgd2l0aCBuZWVkZWQgbGlicmFyaWVzCnBrZ3MgPC0gYygibHVicmlkYXRlIiwgInN0cmluZ3IiLCJ0aWR5dmVyc2UiLCAicmVhZHIiLCAKICAgICAgICAgICJmYXN0RHVtbWllcyIsICJyZXRpY3VsYXRlIiwgImdncGxvdDIiLCAiTWV0cmljcyIsICJWSU0iKQoKIyBMb2FkIGVhY2ggbGlzdGVkIGxpYnJhcnkgYW5kIGNoZWNrIGlmIGl0IGlzIGluc3RhbGxlZCBhbmQgaW5zdGFsbCBpZiBuZWNlc3NhcnkKZm9yIChwa2cgaW4gcGtncykgewogIGlmICghcmVxdWlyZShwa2csIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgIGluc3RhbGwucGFja2FnZXMocGtnKQogICAgbGlicmFyeShwa2csIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKICB9Cn0KYGBgCgojIyMgVm9yYmVyZWl0ZXRlIERhdGVuc8OkdHplIGxhZGVuCgotICAgV2V0dGVyZGF0ZW4gd3VyZGVuIGluICJEYXRlbmF1ZmJlcmVpdHVuZ19XZXR0ZXIuUm1kIiB2b3JiZXJlaXRldAotICAgRmVpZXJ0YWdlZGF0ZW4gd3VyZGVuIGluICJEYXRlbmF1ZmJlcmVpdHVuZ19GZWllcnRhZ2UuUiIgdm9yYmVyZWl0ZXQKLSAgIFNjaHVsZmVyaWVuIHd1cmRlbiBpbiAiRGF0ZW5hdWZiZXJlaXR1bmdfU2NodWxmZXJpZW4uUiIgdm9yYmVyZWl0ZXQKLSAgIFVtc2F0emRhdGVuIHd1cmRlbiBpbiAiRGF0ZW5hdWZiZXJlaXR1bmdfVW1zYXR6LlIiIHZvcmJlcmVpdGV0CgpgYGB7cn0KIyBMYWRlIERhdGVuCmxvYWQoInBqX3dldHRlcl9kdW1teS5SZGEiKQpwal93ZXR0ZXIgPC0gcGpfd2V0dGVyX2R1bW15CiAgCmxvYWQoImtpd29EVC5SZGEiKQpwal9raXdvIDwtIGtpd29EVAogIApsb2FkKCJwal91bXNhdHouUmRhIikKCmxvYWQoInNjaHVsZmVyaWVuLlJkYSIpCnBqX3NjaHVsZmVyaWVuIDwtIHNjaHVsZmVyaWVuCgojIEVyc3RlIEJldHJhY2h0dW5nIGRlciBEYXRlbgojc3VtbWFyeShwal93ZXR0ZXIpCiNzdW1tYXJ5KHBqX2tpd28pCiNzdW1tYXJ5KHBqX3Vtc2F0eikKYGBgCgojIyMgYWxsRGF0YV9kdW1teQoKYGBge3J9CiMgTWVyZ2UgZXJzdGVsbHQgYXV0b21hdGlzY2ggZGllIFNjaG5pdHRtZW5nZQojIERlciBadXNhdHogYWxsLnggPSBUUlVFIHNvcmd0IGRhZsO8ciwgZGFzcyBrZWluZSBaZWlsZW4gKGJhc2llcmVuZCBhdWYgRGF0ZW5zYXR6IHgpIHdlZ2dlbMO2c2NodCB3ZXJkZW4KIyBXZXR0ZXJkYXRlbiBuYWNoIERhdHVtIGhpbnp1ZsO8Z2VuCnBqX3Vtc2F0el93ZXR0ZXIgPC0gbWVyZ2UocGpfdW1zYXR6LCBwal93ZXR0ZXIsIGJ5PSJEYXR1bSIsIGFsbC54ID0gVFJVRSkKCiMgU2NodWxmZXJpZW4gbmFjaCBEYXR1bSBoaW56dWbDvGdlbgpwal91bXNhdHpfd2V0dGVyX2ZlcmllbiA8LSBtZXJnZShwal91bXNhdHpfd2V0dGVyLCBwal9zY2h1bGZlcmllbiwgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQoKIyBLaVdvIG5hY2ggRGF0dW0gaGluenVmw7xnZW4KYWxsRGF0YSA8LSBtZXJnZShwal91bXNhdHpfd2V0dGVyX2ZlcmllbiwgcGpfa2l3bywgYnk9IkRhdHVtIiwgYWxsLnggPSBUUlVFKQoKIyBhdWYgZmVobGVuZGUgV2VydGUgw7xiZXJwcsO8ZmVuOgphbGxEYXRhX25hIDwtIGFsbERhdGEgJT4lCiAgYWdncihjb21iaW5lZD1UUlVFLCBudW1iZXJzPVRSVUUpCgojIEltcHV0YXRpb24gVGVtcGVyYXR1ciB1bmQgV2luZHN0YWVya2UKIyBBa3R1ZWxsOiAiRGF0ZW5zcGVuZGUiIHZvbSBXZXJ0IHZvbSBWb3J0YWcKIyBaSUVMOiBNaXR0ZWx3ZXJ0IGF1cyBUZW1wZXJhdHVyIHZvbiBWb3J0YWcgdW5kIFRhZyBkYW5hY2ggLT4gQXJtYW5kbyEgOikKYWxsRGF0YSA8LSBhbGxEYXRhICU+JSAgCiAgaG90ZGVjayh2YXJpYWJsZSA9IGMoIlRlbXBlcmF0dXIiLCAiV2luZHN0YWVya2UiKSwKICAgICAgICAgIG9yZF92YXIgPSAiRGF0dW0iKQoKI2ltcHV0aWVydGUgV2VydGUgZ3JhcGhpc2NoIMO8YmVycHLDvGZlbjoKZ2dwbG90KGFsbERhdGEpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gRGF0dW0sIHkgPSBUZW1wZXJhdHVyLCBjb2xvciA9IFRlbXBlcmF0dXJfaW1wKSkKZ2dwbG90KGFsbERhdGEpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gRGF0dW0sIHkgPSBXaW5kc3RhZXJrZSwgY29sb3IgPSBXaW5kc3RhZXJrZV9pbXApKQoKIyBOQSBXZXR0ZXJjb2RlcyB6dSAwLCBkYSBTcGFsdGUgV0NfTkEgYW5naWJ0LCB3byBXZXR0ZXJjb2RlcyBnZWZlaGx0IGhhYmVuCiMgU3BhbHRlbiAxMiAtMjQKCiMgZGFzIGdsZWljaGUgZ2lsdCBiZWkgZGVyIEJld8O2bGt1bmcKIyBTcGFsdGVuIDI2IC0gMjkKCiMgd2VpdGVyZSBOQSBtaXQgMCBmw7xsbGVuLCBkb3J0IHdvIGVzIFNpbm4gZXJnaWJ0ICAKCmFsbERhdGEgPC0gYWxsRGF0YSAlPiUKICAgIG11dGF0ZV9hdChjKDEyOjM0KSwgfnJlcGxhY2UoLiwgaXMubmEoLiksIDApKQoKIyBkdW1teSBjb2RpbmcgZGVyIFdvY2hlbnRhZ2UKYWxsRGF0YV9kdW1teSA8LSBkdW1teV9jb2xzKGFsbERhdGEsIHNlbGVjdF9jb2x1bW5zID0gIldvY2hlbnRhZyIpCgphbGxEYXRhX2R1bW15JHllYXIgPC0geWVhcihhbGxEYXRhX2R1bW15JERhdHVtKQphbGxEYXRhX2R1bW15JG1vbnRoIDwtIG1vbnRoKGFsbERhdGFfZHVtbXkkRGF0dW0pCmFsbERhdGFfZHVtbXkkZGF5IDwtIGRheShhbGxEYXRhX2R1bW15JERhdHVtKQoKYWxsRGF0YV9kdW1teSREYXR1bSA8LSBOVUxMCgpzdW1tYXJ5KGFsbERhdGFfZHVtbXkpCnNhdmUoYWxsRGF0YV9kdW1teSwgZmlsZT0icHJvamVjdERhdGFfZHVtbXkuUmRhIikKYGBgCgojIyMgVGVzdGRhdGVuc2F0egoKYGBge3J9CiMgRXJzdGVsbGUgZWluZW4gbGVlcmVuIERhdGFmcmFtZSBtaXQgZWluZXIgU3BhbHRlIGbDvHIgZGFzIERhdHVtCnRlc3REYXRlblNhdHogPC0gZGF0YS5mcmFtZShEYXR1bSA9IGNoYXJhY3RlcigpKQoKIyBFcnN0ZWxsZSBlaW5lIFNlcXVlbnogdm9uIERhdGVuIGltIGFuZ2VnZWJlbmVuIFplaXRyYXVtCmRhdHVtX3NlcXVlbnogPC0gc2VxKGZyb20gPSBhcy5EYXRlKCIyMDE5LTA2LTA5IiksCiAgICAgICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZSgiMjAxOS0wNy0zMCIpLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJkYXlzIikKCiMgRsO8Z2UgZGllIERhdGVuIGRlciBTZXF1ZW56IGRlbSBEYXRhZnJhbWUgaGluenUKdGVzdERhdGVuU2F0eiA8LSByYmluZCh0ZXN0RGF0ZW5TYXR6LCBkYXRhLmZyYW1lKERhdHVtID0gZGF0dW1fc2VxdWVueikpCnRlc3REYXRlblNhdHokV29jaGVudGFnIDwtIHdlZWtkYXlzKHRlc3REYXRlblNhdHokRGF0dW0pCnRlc3REYXRlblNhdHogPC0gbWVyZ2UodGVzdERhdGVuU2F0eiwgcGpfd2V0dGVyLCBieT0iRGF0dW0iLCBhbGwueCA9IFRSVUUpCnRlc3REYXRlblNhdHogPC0gbWVyZ2UodGVzdERhdGVuU2F0eiwgcGpfc2NodWxmZXJpZW4sIGJ5PSJEYXR1bSIsIGFsbC54ID0gVFJVRSkKdGVzdERhdGVuU2F0eiA8LSBtZXJnZSh0ZXN0RGF0ZW5TYXR6LCBwal9raXdvLCBieT0iRGF0dW0iLCBhbGwueCA9IFRSVUUpCiMjIyBUZXN0ZGF0ZW5zYXR6ICMjIwoKdGVzdERhdGVuU2F0eiA8LSB0ZXN0RGF0ZW5TYXR6ICU+JSAKICBob3RkZWNrKHZhcmlhYmxlID0gYygiVGVtcGVyYXR1ciIsICJXaW5kc3RhZXJrZSIpLAogICAgICAgICAgb3JkX3ZhciA9ICJEYXR1bSIpCgojaW1wdXRpZXJ0ZSBXZXJ0ZSB2b24gdGVzdERhdGVuU2F0eiBncmFwaGlzY2ggw7xiZXJwcsO8ZmVuOgpnZ3Bsb3QodGVzdERhdGVuU2F0eikgKwogIGdlb21fcG9pbnQoYWVzKHggPSBEYXR1bSwgeSA9IFRlbXBlcmF0dXIsIGNvbG9yID0gVGVtcGVyYXR1cl9pbXApKQpnZ3Bsb3QodGVzdERhdGVuU2F0eikgKwogIGdlb21fcG9pbnQoYWVzKHggPSBEYXR1bSwgeSA9IFdpbmRzdGFlcmtlLCBjb2xvciA9IFdpbmRzdGFlcmtlX2ltcCkpCgp0ZXN0RGF0ZW5TYXR6IDwtIHRlc3REYXRlblNhdHogJT4lCiAgICBtdXRhdGVfYXQoYyg0OjI2KSwgfnJlcGxhY2UoLiwgaXMubmEoLiksIDApKQoKIyBkdW1teSBjb2RpbmcgZGVyIFdvY2hlbnRhZ2UKdGVzdERhdGVuU2F0eiA8LSBkdW1teV9jb2xzKHRlc3REYXRlblNhdHosIHNlbGVjdF9jb2x1bW5zID0gIldvY2hlbnRhZyIpCgp0ZXN0RGF0ZW5TYXR6JHllYXIgPC0geWVhcih0ZXN0RGF0ZW5TYXR6JERhdHVtKQp0ZXN0RGF0ZW5TYXR6JG1vbnRoIDwtIG1vbnRoKHRlc3REYXRlblNhdHokRGF0dW0pCnRlc3REYXRlblNhdHokZGF5IDwtIGRheSh0ZXN0RGF0ZW5TYXR6JERhdHVtKQoKdGVzdERhdGVuU2F0eiREYXR1bSA8LSBOVUxMCgpzdW1tYXJ5KHRlc3REYXRlblNhdHopCnNhdmUodGVzdERhdGVuU2F0eiwgZmlsZT0iRGF0ZW5hdWZiZXJlaXR1bmdfVGVzdGRhdGVuLlJkYSIpCmBgYAoKIyMjIEZlYXR1cmVzICYgTGFiZWxzCgpgYGB7cn0KZmVhdHVyZXMgPC0gYygiZGF5IiwgICAgICAgICAgICAgICAgICAgICAgICAgICAibW9udGgiLCAgICAgICAgICAgICAgICAgICAgICAgICAieWVhciIsCiAgICAgICAgICAgICAgIldpbmRzdGFlcmtlIiwgICAgICAgICAgICAgICAgICAgIlRlbXBlcmF0dXIiLCAgICAgICAgICAgICAgICAgICAgIldDX0Jld8O2bGt1bmdfYWJuZWhtZW5kIiwKICAgICAgICAgICAgICAiV0NfQmV3w7Zsa3VuZ19nbGVpY2hibGVpYmVuZCIsICAgIldDX0Jld8O2bGt1bmdfbmljaHRfYmVvYmFjaHRldCIsICJXQ19CZXfDtmxrdW5nX3p1bmVobWVuZCIsCiAgICAgICAgICAgICAgIldDX0R1bnN0X1N0YXViIiwgICAgICAgICAgICAgICAgIldDX0VyZWlnbmlzc2VfbGV0enRlX2giLCAgICAgICAgIldDX0dld2l0dGVyIiwKICAgICAgICAgICAgICAiV0NfTmViZWxfRWlzbmViZWwiLCAgICAgICAgICAgICAiV0NfUmVnZW4iLCAgICAgICAgICAgICAgICAgICAgICAiV0NfU2NoYXVlciIsCiAgICAgICAgICAgICAgIldDX1NjaG5lZSIsICAgICAgICAgICAgICAgICAgICAgIldDX1NwcsO8aHJlZ2VuIiwgICAgICAgICAgICAgICAgICJXQ19Ucm9ja2VuZXJlaWduaXNzZSIsCiAgICAgICAgICAgICAgIldDX05BIiwgICAgICAgICAgICAgICAgICAgICAgICAgIkJld29lbGt1bmdzZ3JhZF9nZXJpbmciLCAgICAgICAgIkJld29lbGt1bmdzZ3JhZF9rZWluZSIsCiAgICAgICAgICAgICAgIkJld29lbGt1bmdzZ3JhZF9taXR0ZWwiLCAgICAgICAgIkJld29lbGt1bmdzZ3JhZF9zdGFyayIsICAgICAgICAgIkJld29lbGt1bmdzZ3JhZF9OQSIsCiAgICAgICAgICAgICAgIlNjaHVsZmVyaWVuIiwgICAgICAgICAgICAgICAgICAgIktpZWxlcldvY2hlIiwgICAgICAgICAgICAgICAgICAgIldvY2hlbnRhZ19UdWVzZGF5IiwgICAgICAgICAgCiAgICAgICAgICAgICAgIldvY2hlbnRhZ19UaHVyc2RheSIsCiAgICAgICAgICAgICAgIldvY2hlbnRhZ19GcmlkYXkiLCAgICAgICAgICAgICAgIldvY2hlbnRhZ19XZWRuZXNkYXkiLCAgICAgICAgICAgIldvY2hlbnRhZ19Nb25kYXkiLAogICAgICAgICAgICAgICJXb2NoZW50YWdfU2F0dXJkYXkiLCAgICAgICAgICAgICJXb2NoZW50YWdfU3VuZGF5IikKCmxhYmVscyA8LSBjKCJCcm90IiwgIkJyw7Z0Y2hlbiIsICJDcm9pc3NhbnQiLCAiS29uZGl0b3JlaSIsICJLdWNoZW4iKQpgYGAKCiMjIyBTZWxlY3Rpb24gb2YgVHJhaW5pbmcsIFZhbGlkYXRpb24gYW5kIFRlc3QgRGF0YQoKYGBge3J9CiMgU2V0dGluZyB0aGUgcmFuZG9tIGNvdW50ZXIgdG8gYSBmaXhlZCB2YWx1ZSwgc28gdGhlIHJhbmRvbSBpbml0aWFsaXphdGlvbiBzdGF5cyB0aGUgc2FtZSAodGhlIHJhbmRvbSBzcGxpdCBpcyBhbHdheXMgdGhlIHNhbWUpCnNldC5zZWVkKDEpCgphc3NpZ25tZW50IDwtIHNhbXBsZSgxOjIsIHNpemUgPSBucm93KGFsbERhdGFfZHVtbXkpLCBwcm9iID0gYyguOCwgLjIpLCByZXBsYWNlID0gVFJVRSkKCiMgQ3JlYXRlIHRyYWluaW5nLCB2YWxpZGF0aW9uIGFuZCB0ZXN0IGRhdGEgZm9yIHRoZSBmZWF0dXJlcyBhbmQgdGhlIGxhYmVscwp0cmFpbmluZ19mZWF0dXJlcyA8LSBhbGxEYXRhX2R1bW15W2Fzc2lnbm1lbnQgPT0gMSwgZmVhdHVyZXNdICAgIAp0cmFpbmluZ19sYWJlbHMgPC0gYWxsRGF0YV9kdW1teVthc3NpZ25tZW50ID09IDEsIGxhYmVsc10gICAgCnRyYWluaW5nX2xhYmVscyA8LSBhcy5kYXRhLmZyYW1lKHRyYWluaW5nX2xhYmVscykKCnZhbGlkYXRpb25fZmVhdHVyZXMgPC0gYWxsRGF0YV9kdW1teVthc3NpZ25tZW50ID09IDIsIGZlYXR1cmVzXSAgCnZhbGlkYXRpb25fbGFiZWxzIDwtIGFsbERhdGFfZHVtbXlbYXNzaWdubWVudCA9PSAyLCBsYWJlbHNdICAKdmFsaWRhdGlvbl9sYWJlbHMgPC0gYXMuZGF0YS5mcmFtZSh2YWxpZGF0aW9uX2xhYmVscykKCnRlc3RpbmdfZmVhdHVyZXMgPC0gdGVzdERhdGVuU2F0eiAlPiUgc2VsZWN0KGZlYXR1cmVzKQoKI2FyZSB0aGVyZSBhbnkgbWlzc2luZyB2YWx1ZXM/CnRhYmxlKGlzLm5hKHRyYWluaW5nX2ZlYXR1cmVzKSkKdGFibGUoaXMubmEodmFsaWRhdGlvbl9mZWF0dXJlcykpCnRhYmxlKGlzLm5hKHRlc3RpbmdfZmVhdHVyZXMpKQojc3VtbWFyeShhbGxEYXRhX2R1bW15KQpgYGAKCiMjIyBNb2RlbGwgYXVmc3RlbGxlbiBpbiBQeXRob24KCmBgYHtweXRob259CmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgdGVuc29yZmxvdyBhcyB0Zgpmcm9tIHRlbnNvcmZsb3cua2VyYXMubW9kZWxzIGltcG9ydCBTZXF1ZW50aWFsCmZyb20gdGVuc29yZmxvdy5rZXJhcy5sYXllcnMgaW1wb3J0IElucHV0TGF5ZXIsIERlbnNlLCBCYXRjaE5vcm1hbGl6YXRpb24sIERyb3BvdXQKZnJvbSB0ZW5zb3JmbG93LmtlcmFzLm9wdGltaXplcnMgaW1wb3J0IEFkYW0KCiMgVGhlIGFyZ3VtZW50ICJpbnB1dF9zaGFwZSIgZm9yIHRoZSBkZWZpbml0aW9uIG9mIHRoZSBpbnB1dCBsYXllciBtdXN0IGluY2x1ZGUgCiMgdGhlIG51bWJlciBvZiBpbnB1dCB2YXJpYWJsZXMgKGZlYXR1cmVzKSB1c2VkIGZvciB0aGUgbW9kZWwuIAojIFRvIGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlIHRoaXMgbnVtYmVyIHdlIHVzZSB0aGUgZnVuY3Rpb24gYHIudHJhaW5pbmdfZmVhdHVyZXMua2V5cygpYCwgCiMgd2hpY2ggcmV0dXJucyB0aGUgbGlzdCBvZiB2YXJpYWJsZSBuYW1lcyBvZiB0aGUgZGF0YWZyYW1lIGB0cmFpbmluZ19mZWF0dXJlc2AuCiMgVGhlbiwgdGhlIGZ1bnRpb24gYGxlbigpYCByZXR1cm5zIHRoZSBsZW5ndGggb2YgdGhpcyBsaXN0IG9mIHZhcmlhYmxlIG5hbWVzIAojIChpLmUuIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGluIHRoZSBpbnB1dCkKCm1vZGVsID0gU2VxdWVudGlhbChbCiAgSW5wdXRMYXllcihpbnB1dF9zaGFwZSA9IChsZW4oci50cmFpbmluZ19mZWF0dXJlcy5rZXlzKCkpLCApKSwKICBCYXRjaE5vcm1hbGl6YXRpb24oKSwKICBEZW5zZShsZW4oci50cmFpbmluZ19mZWF0dXJlcy5rZXlzKCkpLCBhY3RpdmF0aW9uID0gJ3N3aXNoJyksCiAgRHJvcG91dCgwLjIpLAogIERlbnNlKGxlbihyLnRyYWluaW5nX2ZlYXR1cmVzLmtleXMoKSksIGFjdGl2YXRpb24gPSAnc3dpc2gnKSwKICBEcm9wb3V0KDAuMiksCiAgRGVuc2UobGVuKHIudHJhaW5pbmdfZmVhdHVyZXMua2V5cygpKSwgYWN0aXZhdGlvbiA9ICdzd2lzaCcpLAogIERlbnNlKDUpCl0pCgojIEF1c2dhYmUgZWluZXIgWlVzYW1tZW5mYXNzdW5nIHp1ciBGb3JtIGRlcyBNT2RlbGxzLCBkYXMgZ2VzY2jDpHR6dCB3aXJkIChuaWNodCBub3R3ZW5kaWcpCiNtb2RlbC5zdW1tYXJ5KCkKYGBgCgojIyMgU2Now6R0enVuZyBkZSBuZXVyb25hbGVuIE5ldHplcwoKYGBge3B5dGhvbn0KIyBkZWZpbml0aW9uIG9mIHRoZSBsb3NzIGZ1bmN0aW9uIGFuZCB0aGUgb3B0aW1hemF0aW9uIGZ1bmN0aW9uIHdpdGggaHlwZXJwYXJhbWV0ZXJzCm1vZGVsLmNvbXBpbGUobG9zcz0ibWFwZSIsIG9wdGltaXplcj1BZGFtKGxlYXJuaW5nX3JhdGU9MC4wMDEpKQoKI1NjaMOkdHp1bmcgZGVzIE1vZGVsbHMKaGlzdG9yeSA9IG1vZGVsLmZpdChyLnRyYWluaW5nX2ZlYXR1cmVzLCByLnRyYWluaW5nX2xhYmVscywgZXBvY2hzID0gMjUwLAogICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fZGF0YSA9IChyLnZhbGlkYXRpb25fZmVhdHVyZXMsIHIudmFsaWRhdGlvbl9sYWJlbHMpLCB2ZXJib3NlID0gMCkKICAgICAgICAgICAgICAgICAgICAKbW9kZWwuc2F2ZSgicHl0aG9uX21vZGVsLmg1IikKYGBgCgojIyMgZ3JhcGhpc2NoZSBBdXNnYWJlIGRlciBNb2RlbGxvcHRpbWllcnVuZwoKYGBge3J9CiMgR3JhcGhpc2NoZSBBdXNnYWJlIGRlciBNb2RlbGxvcHRpbWllcnVuZwoKI2NyZWF0ZSBkYXRhCmRhdGEgPC0gZGF0YS5mcmFtZSh2YWxfbG9zcyA9IHVubGlzdChweSRoaXN0b3J5JGhpc3RvcnkkdmFsX2xvc3MpLAogICAgICAgICAgICAgICAgICAgbG9zcyA9IHVubGlzdChweSRoaXN0b3J5JGhpc3RvcnkkbG9zcykpCgpnZ3Bsb3QoZGF0YVstKDE6MTApLCBdKSsKICBnZW9tX2xpbmUoYWVzKHggPSAxOmxlbmd0aCh2YWxfbG9zcyksIHkgPSB2YWxfbG9zcywgY29sb3VyID0gIlZhbGlkYXRpb24gTG9zcyIpKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gMTpsZW5ndGgobG9zcyksIHkgPSBsb3NzLCBjb2xvdXIgPSAiVHJhaW5pbmcgTG9zcyIpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJUcmFpbmluZyBMb3NzIj0iYmx1ZSIsICJWYWxpZGF0aW9uIExvc3MiID0gInJlZCIpKSArCiAgbGFicyh0aXRsZSA9ICJMb3NzIEZ1bmN0aW9uIFZhbHVlcyBEdXJpbmcgT3B0aW1hemF0aW9uIikgKwogIHhsYWIoIkl0ZXJhdGlvbiBOdW1iZXIiKSArCiAgeWxhYigiTG9zcyIpCmBgYAoKIyMjIEF1c3dlcnR1bmcgZGVyIFNjaMOkdHplcmdlYm5pc3NlCgpgYGB7cn0KIyBTY2jDpHR6dW5nIGRlciAobm9ybWllcnRlbikgUHJlaXNlIGbDvHIgZGllIFRyYWluaW5ncy0gdW5kIFRlc3RkYXRlbgp0cmFpbmluZ19wcmVkaWN0aW9ucyA8LSBweSRtb2RlbCRwcmVkaWN0KHRyYWluaW5nX2ZlYXR1cmVzKQp2YWxpZGF0aW9uX3ByZWRpY3Rpb25zIDwtIHB5JG1vZGVsJHByZWRpY3QodmFsaWRhdGlvbl9mZWF0dXJlcykKdGVzdGluZ19wcmVkaWN0aW9ucyA8LSBweSRtb2RlbCRwcmVkaWN0KHRlc3RpbmdfZmVhdHVyZXMpCgojIFZlcmdsZWljaCBkZXIgR8O8dGVrcml0ZXJpZW4gZsO8ciBkaWUgVHJhaW5naW5ncy0gdW5kIFRlc3RkYXRlbgphIDwtIGZvcm1hdChtYXBlKHRyYWluaW5nX2xhYmVsc1ssMV0sIHRyYWluaW5nX3ByZWRpY3Rpb25zWywxXSkqMTAwLCBkaWdpdHM9MywgbnNtYWxsPTIpCmIgPC0gZm9ybWF0KG1hcGUodHJhaW5pbmdfbGFiZWxzWywyXSwgdHJhaW5pbmdfcHJlZGljdGlvbnNbLDJdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKYyA8LSBmb3JtYXQobWFwZSh0cmFpbmluZ19sYWJlbHNbLDNdLCB0cmFpbmluZ19wcmVkaWN0aW9uc1ssM10pKjEwMCwgZGlnaXRzPTMsIG5zbWFsbD0yKQpkIDwtIGZvcm1hdChtYXBlKHRyYWluaW5nX2xhYmVsc1ssNF0sIHRyYWluaW5nX3ByZWRpY3Rpb25zWyw0XSkqMTAwLCBkaWdpdHM9MywgbnNtYWxsPTIpCmUgPC0gZm9ybWF0KG1hcGUodHJhaW5pbmdfbGFiZWxzWyw1XSwgdHJhaW5pbmdfcHJlZGljdGlvbnNbLDVdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVHJhaW5pbmcgRGF0YTE6XHQiLCBhKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBUcmFpbmluZyBEYXRhMjpcdCIsIGIpKQpjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFRyYWluaW5nIERhdGEzOlx0IiwgYykpCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVHJhaW5pbmcgRGF0YTQ6XHQiLCBkKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBUcmFpbmluZyBEYXRhNTpcdCIsIGUsICJcbiIpKQoKZyA8LSBmb3JtYXQobWFwZSh2YWxpZGF0aW9uX2xhYmVsc1ssMV0sIHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDFdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKaCA8LSBmb3JtYXQobWFwZSh2YWxpZGF0aW9uX2xhYmVsc1ssMl0sIHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDJdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKaSA8LSBmb3JtYXQobWFwZSh2YWxpZGF0aW9uX2xhYmVsc1ssM10sIHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDNdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKaiA8LSBmb3JtYXQobWFwZSh2YWxpZGF0aW9uX2xhYmVsc1ssNF0sIHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDRdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKayA8LSBmb3JtYXQobWFwZSh2YWxpZGF0aW9uX2xhYmVsc1ssNV0sIHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDVdKSoxMDAsIGRpZ2l0cz0zLCBuc21hbGw9MikKICAKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBWYWxpZGF0aW9uIERhdGExOlx0IiwgZykpCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVmFsaWRhdGlvbiBEYXRhMjpcdCIsIGgpKQpjYXQocGFzdGUwKCJcbk1BUEUgb24gdGhlIFZhbGlkYXRpb24gRGF0YTM6XHQiLCBpKSkKY2F0KHBhc3RlMCgiXG5NQVBFIG9uIHRoZSBWYWxpZGF0aW9uIERhdGE0Olx0IiwgaikpCmNhdChwYXN0ZTAoIlxuTUFQRSBvbiB0aGUgVmFsaWRhdGlvbiBEYXRhNTpcdCIsIGssICJcbiIpKQoKIyBNZWFuIG9mIFRyYWluaW5nIGFuZCBWYWxpZGF0aW9uIERhdGEgTUFQRQptZWFuVCA8LSBjKGFzLmRvdWJsZShhKSwgYXMuZG91YmxlKGIpLCBhcy5kb3VibGUoYyksIGFzLmRvdWJsZShkKSwgYXMuZG91YmxlKGUpKSAKbWVhblYgPC0gYyhhcy5kb3VibGUoZyksIGFzLmRvdWJsZShoKSwgYXMuZG91YmxlKGkpLCBhcy5kb3VibGUoaiksIGFzLmRvdWJsZShrKSkgCgpjYXQocGFzdGUwKCJcbk1lYW4gVHJhaW5pbmcgTUFQRTogIiwgbWVhbihtZWFuVCksICJcbiIpKQpjYXQocGFzdGUwKCJNZWFuIFZhbGlkYXRpb24gTUFQRTogIiwgbWVhbihtZWFuViksICJcbiIpKQpgYGAKCiMjIyBHcmFmaXNjaGVyIHZlcmdsZWljaCBkZXIgdm9yaGVyZ2VzYWd0ZW4gJiB0YXRzw6RjaGxpY2hlciBQcmVpc2UgZsO8ciBkaWUgVHJhaW5pbmdzLSB1bmQgVmFsaWRpZXJ1bmdzZGF0ZW4KCmBgYHtyfQpkYXRhX3RyYWluIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRyYWluaW5nX3ByZWRpY3Rpb25zWywxXSwgYWN0dWFsID0gdHJhaW5pbmdfbGFiZWxzWywxXSkKZGF0YV92YWwgPC0gZGF0YS5mcmFtZShwcmVkaWN0aW9uID0gdmFsaWRhdGlvbl9wcmVkaWN0aW9uc1ssMV0sIGFjdHVhbCA9IHZhbGlkYXRpb25fbGFiZWxzWywxXSkKZGF0YV90ZXN0IDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRlc3RpbmdfcHJlZGljdGlvbnNbLDFdKQoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBUcmFpbmluZ3NkYXRlbgpnZ3Bsb3QoZGF0YV90cmFpbltdKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFRyYWluaW5nIERhdGEgMSIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCiMgUGxvdCBkZXIgRXJnZWJuaXNzZSBkZXIgVmFsaWRpZXJ1bmdzZGF0ZW4KZ2dwbG90KGRhdGFfdmFsWyxdKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFZhbGlkYXRpb24gRGF0YSAxIikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRlc3RkYXRlbgpnZ3Bsb3QoZGF0YV90ZXN0KSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGxhYnModGl0bGU9IlByZWRpY3Rpb24gZm9yIHRoZSBUZXN0IERhdGEgMSIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIDIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMKCmRhdGFfdHJhaW4yIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRyYWluaW5nX3ByZWRpY3Rpb25zWywyXSwgYWN0dWFsID0gdHJhaW5pbmdfbGFiZWxzWywyXSkKZGF0YV92YWwyIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDJdLCBhY3R1YWwgPSB2YWxpZGF0aW9uX2xhYmVsc1ssMl0pCmRhdGFfdGVzdDIgPC0gZGF0YS5mcmFtZShwcmVkaWN0aW9uID0gdGVzdGluZ19wcmVkaWN0aW9uc1ssMl0pCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRyYWluaW5nc2RhdGVuCmdncGxvdChkYXRhX3RyYWluMikgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKGFjdHVhbCksIHk9YWN0dWFsLCBjb2xvdXIgPSAiQWN0dWFsIFZhbHVlcyIgKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwoIHZhbHVlcyA9IGMoIlByZWRpY3RlZCBWYWx1ZXMiPSJibHVlIiwgIkFjdHVhbCBWYWx1ZXMiPSJyZWQiKSApICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0ZWQgYW5kIEFjdHVhbCBWYWx1ZXMgZm9yIHRoZSBUcmFpbmluZyBEYXRhIDIiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFZhbGlkaWVydW5nc2RhdGVuCmdncGxvdChkYXRhX3ZhbDIpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVmFsaWRhdGlvbiBEYXRhIDIiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikKCiMgUGxvdCBkZXIgRXJnZWJuaXNzZSBkZXIgVGVzdGRhdGVuCmdncGxvdChkYXRhX3Rlc3QyKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGxhYnModGl0bGU9IlByZWRpY3Rpb24gZm9yIHRoZSBUZXN0IERhdGEgMiIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIDMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMKCmRhdGFfdHJhaW4zIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRyYWluaW5nX3ByZWRpY3Rpb25zWywzXSwgYWN0dWFsID0gdHJhaW5pbmdfbGFiZWxzWywzXSkKZGF0YV92YWwzIDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHZhbGlkYXRpb25fcHJlZGljdGlvbnNbLDNdLCBhY3R1YWwgPSB2YWxpZGF0aW9uX2xhYmVsc1ssM10pCmRhdGFfdGVzdDMgPC0gZGF0YS5mcmFtZShwcmVkaWN0aW9uID0gdGVzdGluZ19wcmVkaWN0aW9uc1ssM10pCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRyYWluaW5nc2RhdGVuCmdncGxvdChkYXRhX3RyYWluMykgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKGFjdHVhbCksIHk9YWN0dWFsLCBjb2xvdXIgPSAiQWN0dWFsIFZhbHVlcyIgKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwoIHZhbHVlcyA9IGMoIlByZWRpY3RlZCBWYWx1ZXMiPSJibHVlIiwgIkFjdHVhbCBWYWx1ZXMiPSJyZWQiKSApICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0ZWQgYW5kIEFjdHVhbCBWYWx1ZXMgZm9yIHRoZSBUcmFpbmluZyBEYXRhIDMiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFZhbGlkaWVydW5nc2RhdGVuCmdncGxvdChkYXRhX3ZhbDMpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVmFsaWRhdGlvbiBEYXRhIDMiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikKCiMgUGxvdCBkZXIgRXJnZWJuaXNzZSBkZXIgVGVzdGRhdGVuCmdncGxvdChkYXRhX3Rlc3QzKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGxhYnModGl0bGU9IlByZWRpY3Rpb24gZm9yIHRoZSBUZXN0IERhdGEgMyIpICsKICB4bGFiKCJDYXNlIE51bWJlciIpICsKICB5bGFiKCJQcmljZSBpbiBFVVIiKSAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSA0IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jCgpkYXRhX3RyYWluNCA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB0cmFpbmluZ19wcmVkaWN0aW9uc1ssNF0sIGFjdHVhbCA9IHRyYWluaW5nX2xhYmVsc1ssNF0pCmRhdGFfdmFsNCA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB2YWxpZGF0aW9uX3ByZWRpY3Rpb25zWyw0XSwgYWN0dWFsID0gdmFsaWRhdGlvbl9sYWJlbHNbLDRdKQpkYXRhX3Rlc3Q0IDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRlc3RpbmdfcHJlZGljdGlvbnNbLDRdKQoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBUcmFpbmluZ3NkYXRlbgpnZ3Bsb3QoZGF0YV90cmFpbjQpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVHJhaW5pbmcgRGF0YSA0IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpIAoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBWYWxpZGllcnVuZ3NkYXRlbgpnZ3Bsb3QoZGF0YV92YWw0KSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFZhbGlkYXRpb24gRGF0YSA0IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRlc3RkYXRlbgpnZ3Bsb3QoZGF0YV90ZXN0NCkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0aW9uIGZvciB0aGUgVGVzdCBEYXRhIDQiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSA1IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jCgpkYXRhX3RyYWluNSA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB0cmFpbmluZ19wcmVkaWN0aW9uc1ssNV0sIGFjdHVhbCA9IHRyYWluaW5nX2xhYmVsc1ssNV0pCmRhdGFfdmFsNSA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24gPSB2YWxpZGF0aW9uX3ByZWRpY3Rpb25zWyw1XSwgYWN0dWFsID0gdmFsaWRhdGlvbl9sYWJlbHNbLDVdKQpkYXRhX3Rlc3Q1IDwtIGRhdGEuZnJhbWUocHJlZGljdGlvbiA9IHRlc3RpbmdfcHJlZGljdGlvbnNbLDVdKQoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBUcmFpbmluZ3NkYXRlbgpnZ3Bsb3QoZGF0YV90cmFpbjUpICsKICBnZW9tX2xpbmUoIGFlcyh4PTE6bGVuZ3RoKHByZWRpY3Rpb24pLCB5PXByZWRpY3Rpb24sIGNvbG91ciA9ICJQcmVkaWN0ZWQgVmFsdWVzIiApKSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChhY3R1YWwpLCB5PWFjdHVhbCwgY29sb3VyID0gIkFjdHVhbCBWYWx1ZXMiICkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKCB2YWx1ZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIj0iYmx1ZSIsICJBY3R1YWwgVmFsdWVzIj0icmVkIikgKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIGFuZCBBY3R1YWwgVmFsdWVzIGZvciB0aGUgVHJhaW5pbmcgRGF0YSA1IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpIAoKIyBQbG90IGRlciBFcmdlYm5pc3NlIGRlciBWYWxpZGllcnVuZ3NkYXRlbgpnZ3Bsb3QoZGF0YV92YWw1KSArCiAgZ2VvbV9saW5lKCBhZXMoeD0xOmxlbmd0aChwcmVkaWN0aW9uKSwgeT1wcmVkaWN0aW9uLCBjb2xvdXIgPSAiUHJlZGljdGVkIFZhbHVlcyIgKSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgoYWN0dWFsKSwgeT1hY3R1YWwsIGNvbG91ciA9ICJBY3R1YWwgVmFsdWVzIiApKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCggdmFsdWVzID0gYygiUHJlZGljdGVkIFZhbHVlcyI9ImJsdWUiLCAiQWN0dWFsIFZhbHVlcyI9InJlZCIpICkgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBhbmQgQWN0dWFsIFZhbHVlcyBmb3IgdGhlIFZhbGlkYXRpb24gRGF0YSA1IikgKwogIHhsYWIoIkNhc2UgTnVtYmVyIikgKwogIHlsYWIoIlByaWNlIGluIEVVUiIpCgojIFBsb3QgZGVyIEVyZ2Vibmlzc2UgZGVyIFRlc3RkYXRlbgpnZ3Bsb3QoZGF0YV90ZXN0NSkgKwogIGdlb21fbGluZSggYWVzKHg9MTpsZW5ndGgocHJlZGljdGlvbiksIHk9cHJlZGljdGlvbiwgY29sb3VyID0gIlByZWRpY3RlZCBWYWx1ZXMiICkpICsKICBsYWJzKHRpdGxlPSJQcmVkaWN0aW9uIGZvciB0aGUgVGVzdCBEYXRhIDUiKSArCiAgeGxhYigiQ2FzZSBOdW1iZXIiKSArCiAgeWxhYigiUHJpY2UgaW4gRVVSIikgCmBgYA==